﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;
using BrainTechLLC;
using System.Threading;
using System;
using System.Diagnostics;
using BrainTechLLC.ThreadSafeObjects;
using BrainTechLLC.WPF;

namespace ImageAppDataModels
{
    public partial class FileImage : BaseNotifyPropertyChangedUI, IHasImageBytes, IObjectLock, IFileImage
    {
        public static ThreadSafeCounterLong TotalBytesRead = new ThreadSafeCounterLong();
        public byte[] _imageBytes;

        public virtual byte[] ImageBytes
        {
            get
            {
                if (_deleted)
                {
                    _imageBytes = null;
                    return null;
                }

                if (_imageBytes == null)
                {
                    ImageBytes = EnsureImageBytesLoaded();
                }

                return _imageBytes;
            }
            set
            {
                if (_imageBytes != value)
                {
                    _imageBytes = value;

                    if (_imageBytes != null)
                    {
                        RecentlyUsedFileImages.RecentlyUsedFileBytes.MarkItemRecentlyUsed(this);

                        if (ThumbnailCached != null)
                        {
                            MarkEverythingDirty(false, true, true);
                        }
                        else
                        {
                            MarkEverythingDirty();
                        }
                    }

                    OnPropertyChanged("ImageBytes");
                }

            }
        }

        public void MarkEverythingDirty(bool doNotRaiseBitmapChangedEvent = false, bool doNotMarkBitmapDirty = false, bool doNotMarkThumbnailDirty = false)
        {
            if (!doNotMarkBitmapDirty && _bitmap != null)
            {
                _bitmap.Dispose();
                _bitmap = null;
            }

            if (_transformedBitmap != null)
            {
                _transformedBitmap = null;
            }

            if (_imageSource != null)
            {
                _imageSource = null;
            }

            if (!doNotMarkThumbnailDirty && ThumbnailObject != null)
            {
                ThumbnailObject = null;
            }

            if (!doNotRaiseBitmapChangedEvent)
            {
                OnPropertyChanged("Bitmap");
            }
        }

        public byte[] ImageBytesCached
        {
            get { return _imageBytes; }
            set
            {
                if (_imageBytes != value)
                {
                    _imageBytes = value;

                    if (_imageBytes != null)
                    {
                        RecentlyUsedFileImages.RecentlyUsedFileBytes.MarkItemRecentlyUsed(this);
                        MarkEverythingDirty();
                    }

                    OnPropertyChanged("ImageBytes");
                }
            }
        }

        public void SetImageBytesSilent(byte[] bytes)
        {
            _imageBytes = bytes;

            if (_imageBytes != null)
            {
                // too expensive?
                RecentlyUsedFileImages.RecentlyUsedFileBytes.MarkItemRecentlyUsed(this);
            }
        }

        public byte[] Load()
        {
            if (_imageBytes != null)
                return _imageBytes;

            if (_deleted)
                return null;

            int fileSize = 0;
            int tries = 0;
            byte[] bytes = null;
            bool noGood = true;

            Timing.BeginTiming();

            while (noGood)
            {
                FileLocking.GetFileLock(_fileName);

                try
                {
                    if (_imageBytes != null)
                    {
                        return _imageBytes;
                    }

                    EnterMonitor(_lockObject, 10);

                    try
                    {
                        if (_imageBytes != null)
                            return _imageBytes;

                        try
                        {
                            bytes = DataProvider.ReadAllImageBytes(_fileName);
                            TotalBytesRead.Add(bytes.Length);
                            noGood = false;
                        }
                        catch (FileNotFoundException)
                        {
                            return null;
                        }
                        catch (IOException)
                        {
                            if (tries++ > 5)
                            {
                                throw;
                            }
                            else
                            {
                                Monitor.Exit(_lockObject);
                                Thread.Sleep(300);
                                Console.WriteLine("SHOULD NOT HAPPEN");
                                EnterMonitor(_lockObject, 20);
                            }
                        }
                    }
                    finally
                    {
                        Monitor.Exit(_lockObject);
                    }
                }
                finally
                {
                    FileLocking.ReleaseFileLock(_fileName);

                    if (bytes != null)
                    {
                        fileSize = bytes.Length;
                        Timing.EndTiming((d) => AllStats.GetInstance().AddTime("AverageThumbLoadTime", ((double)fileSize * 1024D) / (double)d));
                    }
                    else
                    {
                        Timing.EndTiming((d) => { });
                    }
                }
            }

            //Console.WriteLine(string.Format("Loaded {0}, {1} KB", _fileName, fileSize / 1024));
            Debug.WriteLine(string.Format("Loaded {0}, {1} KB", _fileName, fileSize / 1024));

            if (ThumbnailCached != null)
            {
                if ((_imageBytes != bytes) && ImageBytesCached == null || bytes.Length != ImageBytesCached.Length)
                {
                    _imageBytes = bytes;

                    if (_imageBytes != null)
                    {
                        RecentlyUsedFileImages.RecentlyUsedFileBytes.MarkItemRecentlyUsed(this);
                    }
                }
            }
            else
            {
                if ((_imageBytes != bytes) && _imageBytes == null || bytes == null || _imageBytes.Length != bytes.Length)
                {
                    _imageBytes = bytes;

                    if (_imageBytes != null)
                    {
                        RecentlyUsedFileImages.RecentlyUsedFileBytes.MarkItemRecentlyUsed(this);
                    }
                }
            }

            return bytes;
        }

        public Bitmap ReadBitmapFromByteStream(byte[] bytes)
        {
            return Image.FromStream(new MemoryStream(bytes), true, false) as Bitmap;
        }

        protected Bitmap SetBitmapFromImageBytes(byte[] bytes)
        {
            Bitmap bitMap = _bitmap;

            if (bytes != null && (_bitmap == null))
            {
            TryAgain:
                if (Monitor.TryEnter(_lockObject, 20))
                {
                    try
                    {
                        if (bytes != null && (_bitmap == null))
                        {
                            bitMap = ReadBitmapFromByteStream(bytes);
                        }
                    }
                    finally
                    {
                        Monitor.Exit(_lockObject);
                    }
                }
                else
                {
                    if (bytes != null && (_bitmap == null))
                    {
                        Thread.Sleep(10);
                        Debug.WriteLine("Trying again");
                        goto TryAgain;
                    }
                    else
                    {
                        bitMap = _bitmap;
                    }
                }
            }

            return bitMap;
        }

        protected void SetBitmapFromImageBytes()
        {
            SetBitmapFromImageBytes(_imageBytes);
        }
    }
}
